package cn.geminis.crypto.csp.soft;

import cn.geminis.crypto.core.key.KeyPair;
import cn.geminis.crypto.core.key.PrivateKey;
import cn.geminis.crypto.core.key.PublicKey;
import cn.geminis.crypto.csp.AbstractDigest;
import cn.geminis.crypto.csp.AbstractSigner;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.DataLengthException;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.PublicKeyFactory;

import java.io.IOException;
import java.util.Objects;

/**
 * @author Allen
 */
public abstract class AbstractSoftSigner extends AbstractSigner {

    protected AbstractDigest digest;
    protected org.bouncycastle.crypto.Signer signer;
    protected CipherParameters privateKeyParams;

    public AbstractSoftSigner(PublicKey publicKey, PrivateKey privateKey) {
        this.digest = createDigest();
        setKeyPair(new KeyPair(publicKey, privateKey));
    }

    @Override
    public KeyPair getKeyPair() {
        return super.keyPair;
    }

    @Override
    public void setKeyPair(KeyPair keyPair) {
        super.setKeyPair(keyPair);
        this.privateKeyParams = this.keyPair.getPrivateKey().getKeyParameter();
        this.signer = this.createSigner();
    }

    protected abstract AbstractDigest createDigest();

    /**
     * 创建内部签名器
     *
     * @return 内部签名器
     */
    protected abstract org.bouncycastle.crypto.Signer createSigner();

    @Override
    public void init(boolean forSign, CipherParameters params) {
        AsymmetricKeyParameter publicKeyParams = null;
        if (!forSign) {
            try {
                publicKeyParams = Objects.isNull(params) ?
                        PublicKeyFactory.createKey(this.verifyPublicKey.getSubjectPublicKeyInfo()) :
                        (AsymmetricKeyParameter) params;
            } catch (IOException e) {
                throw new RuntimeException("解析公钥错误", e);
            }
        }
        this.signer.init(forSign, forSign ? this.privateKeyParams : publicKeyParams);
    }

    @Override
    public void update(byte[] bytes, int inOff, int len) {
        this.signer.update(bytes, inOff, len);
    }

    @Override
    public byte[] generateSignature() throws CryptoException, DataLengthException {
        return this.signer.generateSignature();
    }

    @Override
    public boolean verifySignature(byte[] bytes) {
        return this.signer.verifySignature(bytes);
    }

    @Override
    public void reset() {
        this.signer.reset();
    }

    @Override
    public void close() {
    }

    @Override
    public void setState(byte[] data) {
        this.digest.setEncodedState(data);
    }

    @Override
    public byte[] getState() {
        return this.digest.getEncodedState();
    }
}
